﻿using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading;

namespace Sbartholomeusz.Lib
{
    class HttpUtils
    {
        const int busyWaitTime = 2000;

        /// <summary>
        /// HTTP file download utility
        /// </summary>
        /// <param name="url"></param>
        /// <param name="destinationDir"></param>
        /// <param name="doAsync"></param>
        public static HttpOperationStatus GetFile(Uri url, String destinationDir, Boolean doAsync = false)
        {
            HttpOperationStatus hos;

            if (doAsync)
            {
                hos = GetFileAsync(url, destinationDir);
            }
            else
            {
                hos = GetFileSync(url, destinationDir);
            }

            return hos;
        }

        /// <summary>
        /// Non blocking asynchronous HTTP file download method
        /// </summary>
        /// <param name="url"></param>
        /// <param name="destinationDir"></param>
        private static HttpOperationStatus GetFileAsync(Uri url, String destinationDir)
        {
            Contract.Requires(url != null);
            Contract.Requires(!String.IsNullOrEmpty(destinationDir));

            using (WebClient wc = new WebClient())
            {
                DateTime timeBefore;
                DateTime timeAfter;
                HttpOperationStatus hos = new HttpOperationStatus();

                wc.Proxy = WebRequest.GetSystemWebProxy();
                wc.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;

                while (wc.IsBusy)
                {
                    Thread.Sleep(busyWaitTime);
                }

                try
                {
                    timeBefore = DateTime.Now;
                    wc.DownloadFileAsync(url, destinationDir);
                    timeAfter = DateTime.Now;

                    hos.StatusCode = HttpOperationStatus.Statuses.STATUS_SUCCESS;
                    hos.RequestDuration = (timeAfter - timeBefore).TotalSeconds;
                }
                catch (WebException e)
                {
                    hos.StatusCode = HttpOperationStatus.Statuses.STATUS_ERROR;
                    hos.StatusMessage = "ERROR: Cannot download the requested file ... please ensure you have specified a valid URL";
                    hos.RequestDuration = 0;
                }
                catch (Exception e)
                {
                    hos.StatusCode = HttpOperationStatus.Statuses.STATUS_ERROR;
                    hos.StatusMessage = "ERROR: An unexpected error occurred while downloading the requested file ... " + e.InnerException.ToString() + " ... " + e.StackTrace;
                    hos.RequestDuration = 0;
                }

                return hos;
            }
        }

        /// <summary>
        /// Blocking synchronous HTTP file download method
        /// </summary>
        /// <param name="url"></param>
        /// <param name="destinationDir"></param>
        private static HttpOperationStatus GetFileSync(Uri url, String destinationDir)
        {
            Contract.Requires(url != null);
            Contract.Requires(!String.IsNullOrEmpty(destinationDir));

            using (WebClient wc = new WebClient())
            {


                DateTime timeBefore;
                DateTime timeAfter;
                HttpOperationStatus hos = new HttpOperationStatus();

                wc.Proxy = WebRequest.GetSystemWebProxy();
                wc.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;

                while (wc.IsBusy)
                {
                    Thread.Sleep(busyWaitTime);
                }

                try
                {
                    timeBefore = DateTime.Now;
                    wc.DownloadFile(url, destinationDir);
                    timeAfter = DateTime.Now;

                    hos.StatusCode = HttpOperationStatus.Statuses.STATUS_SUCCESS;
                    hos.RequestDuration = (timeAfter - timeBefore).TotalSeconds;
                }
                catch (WebException e)
                {
                    hos.StatusCode = HttpOperationStatus.Statuses.STATUS_ERROR;
                    hos.StatusMessage = "ERROR: Cannot download the requested file ... please ensure you have specified a valid URL";
                    hos.RequestDuration = 0;
                }
                catch (Exception e)
                {
                    hos.StatusCode = HttpOperationStatus.Statuses.STATUS_ERROR;
                    hos.StatusMessage = "ERROR: An unexpected error occurred while downloading the requested file ... " + e.InnerException.ToString() + " ... " + e.StackTrace;
                    hos.RequestDuration = 0;
                }

                return hos;
            }
        }

        // TODO: Remove
        //static HttpOperationStatus GetFileSyncTask(Uri sourceUrl, String destinationFile)
        //{
        //    using (WebClient wc = new WebClient())
        //    {
        //        HttpOperationStatus hos = new HttpOperationStatus();

        //        wc.Proxy = WebRequest.GetSystemWebProxy();
        //        wc.Proxy.Credentials = CredentialCache.DefaultNetworkCredentials;

        //        wc.DownloadFile(sourceUrl, destinationFile);

        //        hos.StatusCode = HttpOperationStatus.Statuses.STATUS_SUCCESS;
        //        //hos.RequestDuration = (timeAfter - timeBefore).TotalSeconds;

        //        return hos;
        //    }            
        //}


        /// <summary>
        /// Get file name of file to be downloaded
        /// </summary>
        /// <param name="downloadURL"></param>
        /// <returns></returns>
        public static String GetFileName(string downloadURL)
        {
            return System.IO.Path.GetFileName(downloadURL);
        }
    }


    /// <summary>
    /// Web request status class
    /// </summary>
    public class HttpOperationStatus
    {
        public HttpOperationStatus()
        {
            RequestDuration = 0;
            StatusMessage = "";
        }

        //private int _status;

        // Statuses
        public enum Statuses
        {
            STATUS_SUCCESS,
            STATUS_ERROR
        }

        public Statuses StatusCode { get; set; }

        /// <summary>
        /// Status message
        /// Message will be blank if status successful
        /// </summary>
        public string StatusMessage { get; set; }

        /// <summary>
        /// Duration of request in seconds
        /// </summary>
        public double RequestDuration { get; set; }
    }
}
